//@author A0101906R
#include "CmdEdit.h"

using namespace Twentyfifth;
using namespace std;

const std::string CmdEdit::FEEDBACK_EDITOK = "Task successfully edited.\n";
const std::string CmdEdit::FEEDBACK_UNDOEDITOK = "Undo edit successfull.\n";
const std::string CmdEdit::ERROR_INVALIDTASK = "Invalid task type, cannot be add.\n";
const std::string CmdEdit::ERROR_INVALIDSTART = "Invalid start date/time for task. \n";
const std::string CmdEdit::ERROR_INVALIDEND = "Invalid end date/time for task. \n";
const std::string CmdEdit::ERROR_INVALIDDEADLINE = "Invalid deadline for task. \n";
const std::string CmdEdit::ERROR_INVALIDSEQUENCESE = "End time should not be earlier than start time. \n";
const std::string CmdEdit::ERROR_INVALIDSEQUENCEDL = "Deadline has already passed. \n";
const std::string CmdEdit::ERROR_EMPTYDESC = "Task description should not be empty.\n";
//@author A0101906R
CmdEdit::CmdEdit() {
	descEdited = false;
	venueEdited = false;
	done = false;
	startDateEdited = false;
	startTimeEdited = false;
	endDateEdited = false;
	endTimeEdited = false;
	deadlineDateEdited = false;
	deadlineTimeEdited = false;
	cmdType = EDIT;
}
//@author A0101906R
Status CmdEdit::execution() {

	Status editStatus;
	Status tempS;
	DateTime start, end, deadline;
	int id;
	id = task.getId();

	assert(storage != NULL);
	try {
		rememberTask(tempS);
		extractInfo(tempS, start, end, deadline);

		checkExceptionForEdit(tempS, start, end, deadline);
		if (startDateEdited) {
			editStatus = storage->editTask(id, DATE_START, task.getStart());
		}
		if (startTimeEdited) {
			editStatus = storage->editTask(id, TIME_START, task.getStart());
		}
		if (endDateEdited) {
			editStatus = storage->editTask(id, DATE_END, task.getEnd());
		}
		if (endTimeEdited) {
			editStatus = storage->editTask(id, TIME_END, task.getEnd());
		}
		if (deadlineDateEdited) {
			editStatus = storage->editTask(id, DATE_DEADLINE, task.getDeadline());
		}
		if (deadlineTimeEdited) {
			editStatus = storage->editTask(id, TIME_DEADLINE, task.getDeadline());
		}
		if (descEdited) {
			editStatus = storage->editTask(id, DESCRIPTION, task.getDescription());
		}
		if (venueEdited) {
			editStatus = storage->editTask(id, VENUE, task.getVenue());
		}
		if (done) {
			editStatus = storage->editTask(id, DONE, task.isDone());
		}

		setDescForEdit(editStatus);

		task = taskBeforeEdit;
	}
	catch (Status error) {
		editStatus = error;
	}

	return editStatus;
}
//@author A0101906R
void CmdEdit::extractInfo(Status s, DateTime& start, DateTime& end, DateTime& deadline) {
	Task::TaskType taskType;
	DateTime current;

	taskType = s.tasks[0].getTaskType();
	if (s.code != Status::OK) {
		throw s;
	}
	if (taskType == Task::TIMED_TASK) {
		start = s.tasks[0].getStart();
		end = s.tasks[0].getEnd();
		if (startTimeEdited) {
			start.setHour((task.getStart()).getHour());
			start.setMinute((task.getStart()).getMinute());
		}
		if (startDateEdited) {
			start.setDay((task.getStart()).getDay());
			start.setMonth((task.getStart()).getMonth());
			start.setYear((task.getStart()).getYear());
		}
		if (endTimeEdited) {
			end.setHour((task.getEnd()).getHour());
			end.setMinute((task.getEnd()).getMinute());
		}
		if (endDateEdited) {
			end.setDay((task.getEnd()).getDay());
			end.setMonth((task.getEnd()).getMonth());
			end.setYear((task.getEnd()).getYear());
		}
	} else if (taskType == Task::DEADLINE_TASK) {
		deadline = s.tasks[0].getDeadline();
		if (deadlineDateEdited) {
			deadline.setDay((task.getDeadline()).getDay());
			deadline.setMonth((task.getDeadline()).getMonth());
			deadline.setYear((task.getDeadline()).getYear());
		}
		if (deadlineTimeEdited) {
			deadline.setHour((task.getDeadline()).getHour());
			deadline.setMinute((task.getDeadline()).getMinute());
		}
	}
	return;
}
//@author A0101906R
Status CmdEdit::undo(Status::StatusCode c) {
	Status undoStatus;
	assert(storage != NULL);
	try {
		//to delete the edited task, and add in original task
		if (c != Status::OK) {
			throw ERROR_UNSUCCESSFUL;
		}
		storage->deleteTask(task.getId());
		undoStatus = storage->addTask(task);

		setDescForUndo(undoStatus);
	}
	catch (string error) {
		undoStatus.desc = error;
	}
	return undoStatus;
}
//@author A0101906R
void CmdEdit::checkExceptionForEdit(Status s, DateTime start, DateTime end, DateTime deadline) {
	Task::TaskType taskType;
	DateTime current;

	taskType = s.tasks[0].getTaskType();
	if (s.code != Status::OK) {
		throw s;
	}
	if (descEdited && task.getDescription().size() == 0) {
		s.desc = ERROR_EMPTYDESC;
		throw s;
	}
	if (taskType == Task::TIMED_TASK) {

		if (!start.isValidDate(start.getDay(), start.getMonth(), start.getYear())) {
			s.desc = ERROR_INVALIDSTART;
			throw s;
		}
		if (!start.isValidTime(start.getHour(), start.getMinute(), start.getSecond())) {
			s.desc = ERROR_INVALIDSTART;
			throw s;
		}
		if (!end.isValidDate(end.getDay(), end.getMonth(), end.getYear())) {
			s.desc = ERROR_INVALIDEND;
			throw s;
		}
		if (!end.isValidTime(end.getHour(), end.getMinute(), end.getSecond())) {
			s.desc = ERROR_INVALIDEND;
			throw s;
		}
		if (end <= start) {
			s.desc = ERROR_INVALIDSEQUENCESE;
			throw s;
		}
	} else if (taskType == Task::DEADLINE_TASK) {
		current = getCurrentDateTime();
		if (!deadline.isValidDate(deadline.getDay(), deadline.getMonth(), deadline.getYear())) {
			s.desc = ERROR_INVALIDDEADLINE;
			throw s;
		}
		if (!deadline.isValidTime(deadline.getHour(), deadline.getMinute(), deadline.getSecond())) {
			s.desc = ERROR_INVALIDDEADLINE;
			throw s;
		}
		if (deadline < current) {
			s.desc = ERROR_INVALIDSEQUENCEDL;
			throw s;
		}
	}

	return;
}
//@author A0101906R
void CmdEdit::rememberTask(Status& s) {
	int id;
	id = task.getId();
	s = storage->viewTask(id);

	if (s.code != Status::OK) {
		throw s;
	}

	if (s.code == Status::OK) {
		assert(s.tasks.size() != 0);
		taskBeforeEdit = s.tasks[0];
	}
	return;
}
//@author A0101906R
void CmdEdit::setDescForEdit(Status& s) {
	if (s.code == Status::OK) {
		s.desc = FEEDBACK_EDITOK;
	} else {
		s.desc = statusCodeToString(s);
	}
	return;
}
//@author A0101906R
void CmdEdit::setDescForUndo(Status& s) {
	if (s.code == Status::OK) {
		s.desc = FEEDBACK_UNDOEDITOK;
	} else {
		s.desc = statusCodeToString(s);
	}
	return;
}